/*
 *
 *  *    Copyright 2020-2021 Luter.me
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.luter.heimdall.starter.solon.filter;

import com.luter.heimdall.core.config.ConfigManager;
import com.luter.heimdall.core.config.HeimdallProperties;
import com.luter.heimdall.core.exception.HeimdallUnauthenticatedException;
import com.luter.heimdall.core.exception.HeimdallUnauthorizedException;
import com.luter.heimdall.core.fuction.AbcVoidFunction;
import com.luter.heimdall.core.manager.AuthorizationManager;
import com.luter.heimdall.core.token.SimpleToken;
import com.luter.heimdall.core.utils.PathUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import org.noear.solon.core.Aop;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Filter;
import org.noear.solon.core.handle.FilterChain;
import org.slf4j.Logger;

import java.util.List;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * The type Auth way filter.
 *
 * @author luter
 */
@Data
@Accessors(chain = true, fluent = true)
public class HeimdallAuthorizeFilter implements Filter {
    /**
     * The constant log.
     */
    private static final transient Logger log = getLogger(HeimdallAuthorizeFilter.class);
    /**
     * The constant PATH_UTIL.
     */
    private static final PathUtil PATH_UTIL = new PathUtil();

    /**
     * 默认排除常见静态资源
     * <p>
     * "/static/**",
     * <p>
     * "/images/**",
     * <p>
     * "/css/**",
     * <p>
     * "/js/**",
     * <p>
     * "/favicon*"
     */
    public static final String[] DEFAULT_STATIC_RESOURCE = new String[]{
            "/static/**",
            "/images/**",
            "/css/**",
            "/js/**",
            "/favicon*",
            "/error",
    };
    /**
     * 授权异常后处理
     */
    private AbcVoidFunction<Context, FilterChain, Throwable> error;
    /**
     * The Success.
     */
    private AbcVoidFunction<Context, FilterChain, SimpleToken> success;

    /**
     * Instantiates a new Auth way filter.
     */
    public HeimdallAuthorizeFilter() {
    }

    @Override
    public void doFilter(Context ctx, FilterChain chain) throws Throwable {
        final String method = ctx.method();
        final String url = ctx.uri().getPath();
        final HeimdallProperties config = ConfigManager.getConfig();
        final boolean enabled = config.getAuthority().isEnabled();
        final List<String> includePatterns = config.getAuthority().getIncludes();
        final List<String> excludePatterns = config.getAuthority().getExcludes();
        log.debug("[doFilter]::resource = [{}:{}]", method.toUpperCase(), url);
        final boolean matched = enabled && PATH_UTIL.isMatch(includePatterns, url)
                && !PATH_UTIL.isMatch(excludePatterns, url);
        if (matched) {
            try {
                AuthorizationManager authorizationManager = Aop.get(AuthorizationManager.class);
                final SimpleToken currentToken =
                        authorizationManager.getAuthenticationManager().getCurrentToken(true);
                authorizationManager.isAuthorized(currentToken.getDetails(), method, url, true);
                if (null != success) {
                    success.accept(ctx, chain, currentToken);
                }
            } catch (Throwable exception) {
                if (null != error) {
                    error.accept(ctx, chain, exception);
                } else {
                    int status = 500;
                    if (exception instanceof HeimdallUnauthenticatedException) {
                        status = 401;
                    }
                    if (exception instanceof HeimdallUnauthorizedException) {
                        status = 403;
                    }
                    ctx.status(status);
                    ctx.render(exception.getMessage());
                }
            }

        }
        chain.doFilter(ctx);
    }
}
